package com.mangguo.tingjian.account.security;

import com.mangguo.tingjian.account.common.enums.UserConstant;
import com.mangguo.tingjian.account.mapper.AuthorityMapper;
import com.mangguo.tingjian.account.mapper.UserMapper;
import com.mangguo.tingjian.account.entity.Authority;
import com.mangguo.tingjian.account.entity.User;
import org.hibernate.validator.internal.constraintvalidators.hv.EmailValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * Authenticate a user from the database.
 */
@Component("userDetailsService")
public class UserModelDetailsService implements UserDetailsService {

   private final Logger log = LoggerFactory.getLogger(UserModelDetailsService.class);

   private final UserMapper userMapper;

   private final AuthorityMapper authorityMapper;

   public UserModelDetailsService(UserMapper userMapper, AuthorityMapper authorityMapper) {
      this.userMapper = userMapper;
      this.authorityMapper = authorityMapper;
   }

   @Override
   @Transactional
   public UserDetails loadUserByUsername(final String login) {
      log.debug("Authenticating user '{}'", login);

      if (new EmailValidator().isValid(login, null)) {
         return Optional.of(userMapper.selectByEmail(login))
            .map(user -> createSpringSecurityUser(login, user))
            .orElseThrow(() -> new UsernameNotFoundException("User with email " + login + " was not found in the database"));
      }

      String lowercaseLogin = login.toLowerCase(Locale.ENGLISH);
      return Optional.of(userMapper.selectByUserName(lowercaseLogin))
         .map(user -> createSpringSecurityUser(lowercaseLogin, user))
         .orElseThrow(() -> new UsernameNotFoundException("User " + lowercaseLogin + " was not found in the database"));

   }

   private org.springframework.security.core.userdetails.User createSpringSecurityUser(String lowercaseLogin, User user) {
      if(Objects.equals(user.getIsValid(), UserConstant.ValidStatus.IS_NOT_VALID.getStatus())){
         throw new UserNotValidException("User " + lowercaseLogin + " was not valid");
      }
      List<Authority> authorities = authorityMapper.selectByRoleAndDept(user.getRoleId(), user.getDeptId());
      List<GrantedAuthority> grantedAuthorities = authorities.stream()
              .map(authority -> new SimpleGrantedAuthority(authority.getCode()))
              .collect(Collectors.toList());
      return new org.springframework.security.core.userdetails.User(user.getUsername(),
         user.getPassword(),
              grantedAuthorities);
   }
}
